'''
chat room client
autor by chenkai 
finished time 2018/6/14
'''

import socket
import select
import threading
import os
import hashlib
import tkinter as tk
from tkinter import *
from tkinter import scrolledtext
import tkinter.messagebox
import time
import pymysql
##服务器

class App(tk.Tk):

    def __init__(self):
        super().__init__()
        self.ip=StringVar()
        self.port=StringVar()
        self.clients={}
        self.inputs=[]
        self.outputs=[]
        self.s=None
        self.tkgui()

    def tkgui(self):
        self.title('服务端')
        self.geometry('600x600')
        self.l_ip=tk.Label(self,text='服务器ip:')
        self.l_ip.grid(row=0,column=0,padx=10,pady=10,sticky=tk.E)
        self.e_ip=tk.Entry(self,textvariable=self.ip)
        self.e_ip.grid(row=0,column=1,padx=10,pady=10,sticky=tk.W)
        self.ip.set('127.0.0.1')
        
        self.l_port=tk.Label(self,text='服务器端口:')
        self.l_port.grid(row=1,column=0,padx=10,pady=10,sticky=tk.E)
        self.e_port=tk.Entry(self,textvariable=self.port)
        self.e_port.grid(row=1,column=1,padx=10,pady=10,sticky=tk.W)
        self.port.set('12306')
        
        self.btnst=tk.Button(self,text='开启服务',command=self.cthread)
        self.btnst.grid(row=2,column=0,padx=5,pady=5,sticky=tk.E)
        self.btnsp=tk.Button(self,text='断开服务',command=self.stopServer)
        self.btnsp.grid(row=2,column=1,padx=5,pady=5,sticky=tk.W)
        self.btnsp.configure(state='disable')
        
        self.sW=80
        self.sH=30
        self.scr=scrolledtext.ScrolledText(self,width=self.sW,height=self.sH,wrap=tk.WORD)
        self.scr.grid(row=5,column=0,columnspan=2)

    def makelog(self,str_):
        conn = pymysql.connect(host='localhost', user='root', password='root', db='uesr_log', charset='utf8')
        sql = 'insert into msglog (msg,time) values (%s,%s)' 
        cus1 = conn.cursor()
        cus1.execute(sql, (str_, time.ctime()))
        print('log success\n')
        conn.close()
    
    def mk_file_log(self,str_):
        f = open('./log.txt', 'a', encoding='UTF-8', errors='ignore')
        # f.readlines()
        f.write(time.ctime() + '  登陆信息：' + str_ + '  ' + "\n")
        f.close()

    def tcpserve(self):

    ##    if not os.path.exists('d:/chat/'):
    ##        os.makedirs('d:/chat/')
        
        while True:
            try:
                stdinput, stdoutput, stderr = select.select(self.inputs, self.outputs, self.inputs)
            except:
                #print('断开')
                self.clients.clear()
                return
            for obj in stdinput:
                if obj is self.s:
                    try:
                        c,addr=self.s.accept()
                    except:
                        #print('断开')
                        return
                    self.inputs.append(c)
                    self.clients[addr]=c
                    for l in self.clients:
                        try:
                            self.clients[l].sendall((time.ctime()+'{}已登入').format(str(addr)).encode('utf-8'))
                        except:
                            pass
                    #print('{}已登入'.format(str(addr)))
                    str_time=time.ctime()
                    self.scr.insert("insert",str_time.format(str(addr))+"\n")
                    self.scr.insert("insert",'{}已登入\n'.format(str(addr)))
                    self.mk_file_log('{}已登入\n'.format(str(addr)))
                    self.scr.see(tk.END)
                    self.scr.update()

                else:
                    
                    try:
                        acceptmsg=str(obj.recv(1024),encoding='utf-8')
             
                        #print(''.join([acceptmsg,' 客户端口：',str(addr[1])]))
                        #if acceptmsg !="" :
                        #    tts=threading.Thread(target=self.receiv,args=(acceptmsg,))
                        #    tts.start()
                        if acceptmsg !="" :
                            ts = threading.Thread(target=self.makelog,args=(acceptmsg,))
                            # ts.setDaemon(True)
                            ts.start()
                        #accptdata=''.join([' 客户端口：',str(addr[1]),":",acceptmsg])
                        accptdata=''.join([acceptmsg])
                        accptdata=accptdata+'\n'
                        for k in self.clients:
                           self.clients[k].sendall(accptdata.encode('utf-8'))
                        self.scr.insert("insert",accptdata)
                        self.scr.see(tk.END)
                        self.scr.update()
                        
                        if obj not in self.outputs:  
                                self.outputs.append(obj)
                    except ConnectionResetError:
                        adr=obj.getpeername()
                        self.clients={k:v for k,v in self.clients.items() if k!=adr }
                        for b in self.clients:
                            #try:
                            self.clients[b].sendall('{}已退出'.format(str(adr)).encode('utf-8'))
                            #print('{}已退出'.format(str(adr)))s
                        self.mk_file_log('{}已退出\n'.format(str(adr)))
                        self.scr.insert("insert",'{}已退出\n'.format(str(adr)))
                        self.scr.see(tk.END)
                        self.scr.update()
                            #except:
                                ##print('err')
                        if not self.clients:
                            #print('最后一位{}已退出'.format(str(adr)))
                            self.scr.insert("insert",'最后一位：{}已退出\n'.format(str(adr)))
                            self.scr.see(tk.END)
                            self.scr.update()
                        self.inputs.remove(obj)
                            

    ##        with open('d:/chat/c.txt','r') as f:
    ##            accptall=f.read()
    ##            sendmsg=accptall
    ##        c.sendall(sendmsg.encode('utf-8'))
    ##    c.close()


    def main(self):

        self.s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
##        host='127.0.0.1'
##        port=8800
        self.host=self.ip.get()
        self.e_port=self.port.get()
        if not self.host:
            tkinter.messagebox.showinfo('提示','请输入IP地址！')
            self.scr.insert("insert",'开启失败\n')
            self.scr.see(tk.END)
            self.scr.update()
            return

        if not self.e_port:
            tkinter.messagebox.showinfo('提示','请输入端口！')
            self.scr.insert("insert",'开启失败\n')
            self.scr.see(tk.END)
            self.scr.update()
            return
        try:
            self.e_port=int(self.e_port)
        except:
            #print('无效端口！')
            tkinter.messagebox.showinfo('提示','无效端口！')
            self.scr.insert("insert",'开启失败\n')
            self.scr.see(tk.END)
            self.scr.update()
            return
        ##s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1024)
        try:
            self.s.bind((self.host,self.e_port))
        except:
            #print('开启失败')
            self.scr.insert("insert",'开启失败\n')
            self.scr.see(tk.END)
            self.scr.update()
            tkinter.messagebox.showinfo('提示','开启失败,IP或端口错误！')
            return
        self.s.listen(15)
        self.inputs = [self.s]
        self.btnst.configure(state='disable')
        self.btnsp.configure(state='normal')
        self.tcpserve()
       
        
    def cthread(self):
        t=threading.Thread(target=self.main)
        t.start()
        tse=threading.Thread(target=self.file_save)
        tse.start()
        #t.join()
        #print('已开启')
        self.scr.insert("insert",'已开启\n')
        self.scr.see(tk.END)
        self.scr.update()

    def stopServer(self):
        self.scr.insert("insert",'已断开\n')
        self.scr.see(tk.END)
        self.scr.update()
        #print('已断开')
        self.s.close()
        self.clients.clear()
        self.inputs.clear()
        self.outputs.clear()
        self.btnst.configure(state='normal')
        self.btnsp.configure(state='disable')

    def file_op(self,str_):
        client = socket.socket()  # 生成socket连接对象
        ip_port = ("localhost", 6970)  # 地址和端口号
        client.connect(ip_port)  # 连接
        print("服务器已连接")
        content = str_
        if len(content) == 0: return  # 如果传入空字符会阻塞

        if content.startswith("get"):

            client.send(content.encode("utf-8"))  # 传送和接收都是bytes类型

            # 1.先接收长度，建议8192
            server_response = client.recv(1024)
            file_size = int(server_response.decode("utf-8"))

            print("接收到的大小：", file_size)
            # 2.接收文件内容
            client.send("准备好接收".encode("utf-8"))  # 接收确认
            sp=content.split("/")
            filename = "下载/" + sp[len(sp)-1]

            f = open(filename, "wb")
            received_size = 0
            m = hashlib.md5()

            while received_size < file_size:
                size = 0  # 准确接收数据大小，解决粘包
                if file_size - received_size > 1024:  # 多次接收
                    size = 1024
                else:  # 最后一次接收完毕
                    size = file_size - received_size

                data = client.recv(size)  # 多次接收内容，接收大数据
                data_len = len(data)
                received_size += data_len
                print("已接收：", int(received_size / file_size * 100), "%")

                m.update(data)
                f.write(data)

            f.close()

            print("实际接收的大小:", received_size)  # 解码

            # 3.md5值校验
            md5_sever = client.recv(1024).decode("utf-8")
            md5_client = m.hexdigest()
            print("服务器发来的md5:", md5_sever)
            print("接收文件的md5:", md5_client)
            if md5_sever == md5_client:
                print("MD5值校验成功")
            else:
                print("MD5值校验失败")
        client.close()
        return

    def file_save(self):
        server = socket.socket()

        server.bind(("localhost", 6980))  # 绑定监听端口

        server.listen(5)  # 监听

        print("监听开始..")

        while True:
            conn, addr = server.accept()  # 等待连接

            print("conn:", conn, "\naddr:", addr)  # conn连接实例

            while True:
                data = conn.recv(1024)  # 接收
                if not data:  # 客户端已断开
                    print("客户端断开连接")
                    break

                print("收到的命令：", data.decode("utf-8"))
                sp=data.decode("utf-8").split(" ")
                cmd = sp[0]
                filename=sp[1]
                ts = threading.Thread(target=self.mk_file_log, args=(data,))
                # ts.setDaemon(True)
                ts.start()
                if cmd == "get":
                    if os.path.isfile(filename):  # 判断文件存在
                        # 1.先发送文件大小，让客户端准备接收
                        size = os.stat(filename).st_size  # 获取文件大小
                        conn.send(str(size).encode("utf-8"))  # 发送数据长度
                        print("发送的大小：", size)

                        # 2.发送文件内容
                        conn.recv(1024)  # 接收确认

                        m = hashlib.md5()
                        f = open(filename, "rb")
                        for line in f:
                            conn.send(line)  # 发送数据
                            m.update(line)
                        f.close()

                        # 3.发送md5值进行校验
                        md5 = m.hexdigest()
                        conn.send(md5.encode("utf-8"))  # 发送md5值
                        print("md5:", md5)
                elif cmd == "send":
                    #ts = threading.Thread(target=self.file_op,args=("get "+filename,))
                    #ts.start()
                    self.file_op("get "+filename)

        server.close()

if __name__=='__main__':

    app=App()
    app.mainloop()

